/*
 * Copyright (c) 2025 Huawei Device, Inc. Ltd. and <马弓手>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "InAppBrowser.h"
#include "CordovaArgs.h"
#include "CordovaViewController.h"
#include "HttpUrl.h"
#include "cJSON.h"
#include "FileCache.h"
REGISTER_PLUGIN_CLASS(InAppBrowser)

bool InAppBrowser::execute(const string&  action, cJSON* args, CallbackContext cbc)
{
    if(action == "onArKTsResult") {
        return onArKTsResult(args);
    }
    
    if(action == "open") {
        string strWebTag = cbc.getWebTag();
        m_mapWebTagToChanel[strWebTag] = cbc;
        CordovaArgs cordovaArgs(args);
        int nArraySize = cordovaArgs.getSize();
        if(nArraySize > 0)
            m_strUrl = cordovaArgs.getString(0);
        if(nArraySize > 1)
            m_strType = cordovaArgs.optString(1);
        if(nArraySize > 2)
            m_strArg = cordovaArgs.optString(2);
        
        map<string, string> mapKeyValue;
        getArgs(m_strArg, mapKeyValue);
        if(mapKeyValue.find("hidden") != mapKeyValue.end()) {
            string strHidden = mapKeyValue["hidden"];
            if(strHidden == "yes" || strHidden == "YES") {
                PluginResult pluginResult(PluginResult::Status::OK, "");
                pluginResult.setKeepCallback(true);
                cbc.sendPluginResult(pluginResult);
                return true;
            }
        }
        
        if(mapKeyValue.find("title") != mapKeyValue.end()) {
            m_strTitle = mapKeyValue["title"];
        }

        cJSON* pArray = cJSON_CreateArray();
        for(auto it = mapKeyValue.begin(); it != mapKeyValue.end(); it++) {
            cJSON* pItem = cJSON_CreateObject();    
            cJSON_AddStringToObject(pItem, "key", it->first.c_str());
            cJSON_AddStringToObject(pItem, "value", it->second.c_str());
            cJSON_AddItemToArray(pArray, pItem);
        }
        
        string strStatusBarBackgroundColor = m_preferences->getString("StatusBarBackgroundColor", "#ffffff");
        if(!strStatusBarBackgroundColor.empty()) {
            cJSON* pItem = cJSON_CreateObject();    
            cJSON_AddStringToObject(pItem, "key", "barGroundColor");
            cJSON_AddStringToObject(pItem, "value", strStatusBarBackgroundColor.c_str());
            cJSON_AddItemToArray(pArray, pItem);
        }
        
       string strStatusTextColor = m_preferences->getString("StatusBarTextColor", "#000000");
        if(!strStatusTextColor.empty()) {
            cJSON* pItem = cJSON_CreateObject();    
            cJSON_AddStringToObject(pItem, "key", "closebuttoncolor");
            cJSON_AddStringToObject(pItem, "value", strStatusTextColor.c_str());
            cJSON_AddItemToArray(pArray, pItem);
        }
        
        char* pArgs = cJSON_Print(pArray);
        
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "url", m_strUrl.c_str());
        cJSON_AddStringToObject(pTemp, "title", m_strTitle.c_str());
        cJSON_AddStringToObject(pTemp, "target", m_strType.c_str());
        cJSON_AddStringToObject(pTemp, "args", pArgs);
        
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        string result = showWebPage(strArgs, cbc);
        PluginResult pluginResult(PluginResult::Status::OK, result);
        pluginResult.setKeepCallback(true);
        cbc.sendPluginResult(pluginResult);
        free((void*)pStr);
        cJSON_Delete(pTemp);
        free((void*)pArgs);
    }
    
    if(action == "show") {
        if(cJSON_GetArraySize(args) > 0) {
            cJSON* pJson = cJSON_GetArrayItem(args, 0);
            if(pJson->type == cJSON_String) {
                m_strTitle = pJson->valuestring;
            }
        }
        map<string, string> mapKeyValue;
        getArgs(m_strArg, mapKeyValue);
        
        cJSON* pArray = cJSON_CreateArray();
        for(auto it = mapKeyValue.begin(); it != mapKeyValue.end(); it++) {
            cJSON* pItem = cJSON_CreateObject();    
            cJSON_AddStringToObject(pItem, "key", it->first.c_str());
            cJSON_AddStringToObject(pItem, "value", it->second.c_str());
            cJSON_AddItemToArray(pArray, pItem);
        }
        char* pArgs = cJSON_Print(pArray);
        
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "url", m_strUrl.c_str());
        cJSON_AddStringToObject(pTemp, "title", m_strTitle.c_str());
        cJSON_AddStringToObject(pTemp, "target", m_strType.c_str());
        cJSON_AddStringToObject(pTemp, "args", pArgs);
        
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        string result = showWebPage(strArgs, cbc);
        PluginResult pluginResult(PluginResult::Status::OK, result);
        pluginResult.setKeepCallback(true);
        cbc.sendPluginResult(pluginResult);
        free((void*)pStr);
        cJSON_Delete(pTemp);
        free((void*)pArgs);
    }
    
    if(action  == "close" || action == "hide") {
        closeDialog(cbc);
    }
    
    if(action == "loadAfterBeforeload") {
        map<string, string> mapKeyValue;
        getArgs(m_strArg, mapKeyValue);
        
        cJSON* pArray = cJSON_CreateArray();
        for(auto it = mapKeyValue.begin(); it != mapKeyValue.end(); it++) {
            cJSON* pItem = cJSON_CreateObject();    
            cJSON_AddStringToObject(pItem, "key", it->first.c_str());
            cJSON_AddStringToObject(pItem, "value", it->second.c_str());
            cJSON_AddItemToArray(pArray, pItem);
        }
        char* pArgs = cJSON_Print(pArray);
        
        CordovaArgs cordovaArgs(args);
        int nArraySize = cordovaArgs.getSize();
        if(nArraySize > 0)
            m_strUrl = cordovaArgs.getString(0);
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "url", m_strUrl.c_str());
        cJSON_AddStringToObject(pTemp, "title", m_strTitle.c_str());
        cJSON_AddStringToObject(pTemp, "target", m_strType.c_str());
        cJSON_AddStringToObject(pTemp, "args", pArgs);
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        string result = showWebPage(strArgs, cbc);
        PluginResult pluginResult(PluginResult::Status::OK, result);
        pluginResult.setKeepCallback(true);
        cbc.sendPluginResult(pluginResult);
        free((void*)pStr);
        cJSON_Delete(pTemp);
        free((void*)pArgs);
    }
    
    if(action == "injectScriptCode") {
        string strCbId = cbc.getCallbackId();
        m_mapCallBackIdToCbc[strCbId] = cbc;
        
        CordovaArgs cordovaArgs(args);
        int nArraySize = cordovaArgs.getSize();
        string strCode = "";
        bool isCallBack = false;
        if(nArraySize > 0)
            strCode = cordovaArgs.getString(0);
        if(nArraySize > 1)
            isCallBack = cordovaArgs.getBoolean(1);
        if(strCode.empty()) {
            return true;
        }
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "code", strCode.c_str());
        cJSON_AddStringToObject(pTemp, "callbackId", strCbId.c_str());
        if(isCallBack) {
            cJSON_AddTrueToObject(pTemp, "cb");
        } else {
            cJSON_AddFalseToObject(pTemp, "cb");
        }
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        injectScriptCode(strArgs, cbc);
        free((void*)pStr);
        cJSON_Delete(pTemp);
    }
    
    if(action == "injectStyleCode") {
        string strCbId = cbc.getCallbackId();
        m_mapCallBackIdToCbc[strCbId] = cbc;
        
        CordovaArgs cordovaArgs(args);
        int nArraySize = cordovaArgs.getSize();
        string strCode = "";
        bool isCallBack = false;
        if(nArraySize > 0)
            strCode = cordovaArgs.getString(0);
        if(nArraySize > 1)
            isCallBack = cordovaArgs.getBoolean(1);
        if(strCode.empty()) {
            return true;
        }
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "code", strCode.c_str());
        cJSON_AddStringToObject(pTemp, "callbackId", strCbId.c_str());
        if(isCallBack) {
            cJSON_AddTrueToObject(pTemp, "cb");
        } else {
            cJSON_AddFalseToObject(pTemp, "cb");
        }
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        injectStyleCode(strArgs, cbc);
        free((void*)pStr);
        cJSON_Delete(pTemp);
    }
    
    if(action == "injectScriptFile") {
        string strCbId = cbc.getCallbackId();
        m_mapCallBackIdToCbc[strCbId] = cbc;
        
        CordovaArgs cordovaArgs(args);
        int nArraySize = cordovaArgs.getSize();
        string strCode = "";
        bool isCallBack = false;
        if(nArraySize > 0)
            strCode = cordovaArgs.getString(0);
        if(nArraySize > 1)
            isCallBack = cordovaArgs.getBoolean(1);
        if(strCode.empty()) {
            return true;
        }
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "file", strCode.c_str());
        cJSON_AddStringToObject(pTemp, "callbackId", strCbId.c_str());
        if(isCallBack) {
            cJSON_AddTrueToObject(pTemp, "cb");
        } else {
            cJSON_AddFalseToObject(pTemp, "cb");
        }
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        injectScriptFile(strArgs, cbc);
        free((void*)pStr);
        cJSON_Delete(pTemp);
    }
    
    if(action == "injectStyleFile") {
        string strCbId = cbc.getCallbackId();
        m_mapCallBackIdToCbc[strCbId] = cbc;
        
        CordovaArgs cordovaArgs(args);
        int nArraySize = cordovaArgs.getSize();
        string strCode = "";
        bool isCallBack = false;
        if(nArraySize > 0)
            strCode = cordovaArgs.getString(0);
        if(nArraySize > 1)
            isCallBack = cordovaArgs.getBoolean(1);
        if(strCode.empty()) {
            return true;
        }
        cJSON* pTemp = cJSON_CreateObject();    
        cJSON_AddStringToObject(pTemp, "file", strCode.c_str());
        cJSON_AddStringToObject(pTemp, "callbackId", strCbId.c_str());
        if(isCallBack) {
            cJSON_AddTrueToObject(pTemp, "cb");
        } else {
            cJSON_AddFalseToObject(pTemp, "cb");
        }
        char* pStr = cJSON_Print(pTemp);
        string strArgs = pStr;
        injectStyleFile(strArgs, cbc);
        free((void*)pStr);
        cJSON_Delete(pTemp);
    }
    return true;
}

string InAppBrowser::showWebPage(const string strArgs, CallbackContext cbc)
{
    executeArkTs("./InAppBrowser/BrowserAction/openInAppBrowser", 0, strArgs.c_str(), "InAppBrowser", cbc);
    return "OK";
}

void InAppBrowser::closeDialog(CallbackContext cbc)
{
    executeArkTs("./InAppBrowser/BrowserAction/closeInAppBrowser", 0, "close", "InAppBrowser", cbc);
    return;
}

void InAppBrowser::injectScriptCode(const string strArgs,  CallbackContext cbc)
{
    executeArkTs("./InAppBrowser/BrowserAction/injectScriptCodeInAppBrowser", 0, strArgs.c_str(), "InAppBrowser", cbc);
    return;
}

void InAppBrowser::injectScriptFile(const string strArgs,  CallbackContext cbc)
{
    executeArkTs("./InAppBrowser/BrowserAction/injectScriptFileInAppBrowser", 0, strArgs.c_str(), "InAppBrowser", cbc);
    return;
}

void InAppBrowser::injectStyleCode(const string strArgs,  CallbackContext cbc)
{
    executeArkTs("./InAppBrowser/BrowserAction/injectStyleCodeInAppBrowser", 0, strArgs.c_str(), "InAppBrowser", cbc);
    return;
}

void InAppBrowser::injectStyleFile(const string strArgs,  CallbackContext cbc)
{
    executeArkTs("./InAppBrowser/BrowserAction/injectStyleFileInAppBrowser", 0, strArgs.c_str(), "InAppBrowser", cbc);
    return;
}

bool InAppBrowser::onArKTsResult(cJSON* args)
{
    cJSON* content = cJSON_GetObjectItem(args, "content");
    string action = "";
    if(content != NULL) {
        action = content->valuestring;
    }
    
    if(action == "loadstart" 
        || action == "loadstop" 
        || action == "loaderror"
        || action == "exit"
        || action == "message") {
        cJSON* json = cJSON_GetObjectItem(args, "result");
        string webTag = "";  
        string data = "";
        if(json != NULL && json->type == cJSON_Array) {
            int count = cJSON_GetArraySize(json);
            if(count > 0) {
                webTag = cJSON_GetArrayItem(json, 0)->valuestring;
            }
            if(count > 1) {
                data = cJSON_GetArrayItem(json, 1)->valuestring;
            }
        }
        
        if(m_mapWebTagToChanel.find(webTag) == m_mapWebTagToChanel.end()) {
            return true;
        }
        
        cJSON * pObj = cJSON_CreateObject();
        cJSON_AddStringToObject(pObj, "type", action.c_str());
        if(!data.empty()) {
            cJSON * pData = cJSON_Parse(data.c_str());
            if(pData != NULL) {
                cJSON_AddItemToObject(pObj, "data", pData);
            }
        }
        PluginResult pluginResult(PluginResult::Status::OK, pObj);
        pluginResult.setKeepCallback(true);
        if(m_mapWebTagToChanel.find(webTag) != m_mapWebTagToChanel.end()) {
            CallbackContext cbc = m_mapWebTagToChanel[webTag];
            cbc.sendPluginResult(pluginResult);
        }
        cJSON_Delete(pObj);
    }
    
    if(action == "beforeload") {
        cJSON* json = cJSON_GetObjectItem(args, "result");
        string webTag = "";  
        string strUrl;
        if(json != NULL && json->type == cJSON_Array) {
            int count = cJSON_GetArraySize(json);
            if(count > 0) {
                webTag = cJSON_GetArrayItem(json, 0)->valuestring;
            }
            
            if(count > 1) {
                strUrl = cJSON_GetArrayItem(json, 1)->valuestring;
            }
            PluginResult pluginResult(PluginResult::Status::OK, strUrl);
            pluginResult.setKeepCallback(true);
            if(m_mapWebTagToChanel.find(webTag) != m_mapWebTagToChanel.end()) {
                CallbackContext cbc = m_mapWebTagToChanel[webTag];
                cbc.sendPluginResult(pluginResult);
            }
        }
    }
    
    if(action == "customscheme") {
        cJSON* json = cJSON_GetObjectItem(args, "result");
        string webTag = "";  
        string strUrl;
        if(json != NULL && json->type == cJSON_Array) {
            int count = cJSON_GetArraySize(json);
            if(count > 0) {
                webTag = cJSON_GetArrayItem(json, 0)->valuestring;
            }
            
            if(count > 1) {
                strUrl = cJSON_GetArrayItem(json, 1)->valuestring;
            }
            
            cJSON * pObj = cJSON_CreateObject();
            cJSON_AddStringToObject(pObj, "type", action.c_str());
            cJSON_AddStringToObject(pObj, "url", strUrl.c_str());
            PluginResult pluginResult(PluginResult::Status::OK, pObj);
            pluginResult.setKeepCallback(true);
            if(m_mapWebTagToChanel.find(webTag) != m_mapWebTagToChanel.end()) {
                CallbackContext cbc = m_mapWebTagToChanel[webTag];
                cbc.sendPluginResult(pluginResult);
            }
            cJSON_Delete(pObj);
        }
    }
    
    if(action == "download") {
        cJSON* json = cJSON_GetObjectItem(args, "result");
        string webTag = "";  
        string strUrl;
        string strUserAgent;
        string strContentDisposition;
        string strMimetype;
        string strContentLength;
        long lngContentLength = 0;
        if(json != NULL && json->type == cJSON_Array) {
            int count = cJSON_GetArraySize(json);
            if(count > 0) {
                webTag = cJSON_GetArrayItem(json, 0)->valuestring;
            }
            
            if(count > 1) {
                strUrl = cJSON_GetArrayItem(json, 1)->valuestring;
            }
            
            if(count > 2) {
                strUserAgent = cJSON_GetArrayItem(json, 2)->valuestring;
            }
            
            if(count > 3) {
                strContentDisposition = cJSON_GetArrayItem(json, 3)->valuestring;
            }
            
            if(count > 4) {
                strMimetype = cJSON_GetArrayItem(json, 4)->valuestring;
            }
            
            if(count > 5) {
                strContentLength = cJSON_GetArrayItem(json, 5)->valuestring;
                lngContentLength = atoi(strContentLength.c_str());
            }
            
            cJSON * pObj = cJSON_CreateObject();
            cJSON_AddStringToObject(pObj, "type", action.c_str());
            cJSON_AddStringToObject(pObj, "url", strUrl.c_str());
            cJSON_AddStringToObject(pObj, "userAgent", strUserAgent.c_str());
            cJSON_AddStringToObject(pObj, "contentDisposition", strContentDisposition.c_str());
            cJSON_AddStringToObject(pObj, "mimetype", strMimetype.c_str());
            cJSON_AddNumberToObject(pObj, "contentLength", lngContentLength);
            PluginResult pluginResult(PluginResult::Status::OK, pObj);
            pluginResult.setKeepCallback(true);
            
            if(m_mapWebTagToChanel.find(webTag) != m_mapWebTagToChanel.end()) {
                CallbackContext cbc = m_mapWebTagToChanel[webTag];
                cbc.sendPluginResult(pluginResult);
            }
            cJSON_Delete(pObj);
        }
    }
    
    if(action == "callback") {
        cJSON* json = cJSON_GetObjectItem(args, "result");
        string callbackId = "";  
        string message = "";
        if(json != NULL && json->type == cJSON_Array) {
            int count = cJSON_GetArraySize(json);
            if(count > 0) {
                callbackId = cJSON_GetArrayItem(json, 0)->valuestring;
            }
            if(count > 1) {
                message = cJSON_GetArrayItem(json, 1)->valuestring;
            }
            
            if(m_mapCallBackIdToCbc.find(callbackId) != m_mapCallBackIdToCbc.end()) {
                CallbackContext cbc = m_mapCallBackIdToCbc[callbackId];
                if(message.empty()) {
                    PluginResult pluginResult(PluginResult::Status::OK);
                    pluginResult.setKeepCallback(true);
                    cbc.sendPluginResult(pluginResult);
                } else {
                    PluginResult pluginResult(PluginResult::Status::OK, message);
                    pluginResult.setKeepCallback(true);
                    cbc.sendPluginResult(pluginResult);
                }
                m_mapCallBackIdToCbc.erase(callbackId);
            }
        }
    }
    return true;
}

void InAppBrowser::getArgs(const string& strArgs, map<string, string>& mapKeyValue)
{
    vector<string> vecArgs;
    HttpUrl::SplitString(strArgs, ",", vecArgs);
    for(int i=0; i<vecArgs.size(); i++) {
        vector<string> vecKeyValue;
        HttpUrl::SplitString(vecArgs[i], "=", vecKeyValue);
        if(vecKeyValue.size() == 2) {
            string strKey = HttpUrl::trim(vecKeyValue[0]);
            string strValue = HttpUrl::trim(vecKeyValue[1]);
            mapKeyValue[strKey] = strValue;
        }
    }
}